-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[FunctionAttrs] Infer errnomem location when accessing errno
#129067
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[FunctionAttrs] Infer errnomem location when accessing errno
#129067
Conversation
|
@llvm/pr-subscribers-llvm-transforms Author: Antonio Frighetto (antoniofrighetto) ChangesRefine errnomem for a given memory location. In particular, accesses to Full diff: https://github.com/llvm/llvm-project/pull/129067.diff 7 Files Affected:
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index a63e38a7d98ad..bebcb4057acba 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -118,16 +118,31 @@ static void addLocAccess(MemoryEffects &ME, const MemoryLocation &Loc,
ModRefInfo MR, AAResults &AAR) {
// Ignore accesses to known-invariant or local memory.
MR &= AAR.getModRefInfoMask(Loc, /*IgnoreLocal=*/true);
- if (isNoModRef(MR))
+ if (isNoModRef(MR)) {
+ // Mask out errno, can never alias this known-invariant memory location.
+ ME -= ME.errnoMemOnly();
return;
+ }
const Value *UO = getUnderlyingObjectAggressive(Loc.Ptr);
- if (isa<AllocaInst>(UO))
+ if (isa<AllocaInst>(UO)) {
+ ME -= ME.errnoMemOnly();
return;
+ }
if (isa<Argument>(UO)) {
ME |= MemoryEffects::argMemOnly(MR);
return;
}
+ if (auto *CI = dyn_cast<CallInst>(UO)) {
+ if (auto *Callee = CI->getCalledFunction()) {
+ static constexpr auto ErrnoFnNames = {"__errno_location", "_errno",
+ "__errno", "___errno"};
+ if (is_contained(ErrnoFnNames, Callee->getName())) {
+ ME |= MemoryEffects::errnoMemOnly(MR);
+ return;
+ }
+ }
+ }
// If it's not an identified object, it might be an argument.
if (!isIdentifiedObject(UO))
@@ -250,7 +265,22 @@ checkFunctionMemoryAccess(Function &F, bool ThisBody, AAResults &AAR,
if (I.isVolatile())
ME |= MemoryEffects::inaccessibleMemOnly(MR);
+ // Refine memory effects for the given location.
addLocAccess(ME, *Loc, MR, AAR);
+
+ // Last attempt if errnomem has not been inferred yet: accesses larger than
+ // integers cannot alias errno.
+ if (ME.getModRef(IRMemLocation::ErrnoMem) != ModRefInfo::NoModRef) {
+ if (isa<LoadInst>(I) || isa<StoreInst>(I)) {
+ auto *Ty = isa<LoadInst>(I)
+ ? I.getType()
+ : cast<StoreInst>(I).getValueOperand()->getType();
+ if (!Ty->isPtrOrPtrVectorTy() &&
+ Loc->Size != MemoryLocation::UnknownSize &&
+ Loc->Size.getValue() > sizeof(int))
+ ME = ME.getWithoutLoc(IRMemLocation::ErrnoMem);
+ }
+ }
}
return {OrigME & ME, RecursiveArgME};
diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
index 42e0e94c1cee3..fe096fcfc43b3 100644
--- a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
+++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll
@@ -334,7 +334,7 @@ define void @test_inaccessiblememonly_readonly() {
; FNATTRS: Function Attrs: nofree memory(inaccessiblemem: read)
; FNATTRS-LABEL: define void @test_inaccessiblememonly_readonly
; FNATTRS-SAME: () #[[ATTR13:[0-9]+]] {
-; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19:[0-9]+]]
+; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR20:[0-9]+]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: nosync memory(inaccessiblemem: read)
@@ -352,7 +352,7 @@ define void @test_inaccessibleorargmemonly_readonly(ptr %arg) {
; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readonly
; FNATTRS-SAME: (ptr readonly captures(none) [[ARG:%.*]]) #[[ATTR14:[0-9]+]] {
; FNATTRS-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARG]], align 4
-; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR20]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: nosync memory(argmem: read, inaccessiblemem: read)
@@ -372,7 +372,7 @@ define void @test_inaccessibleorargmemonly_readwrite(ptr %arg) {
; FNATTRS-LABEL: define void @test_inaccessibleorargmemonly_readwrite
; FNATTRS-SAME: (ptr writeonly captures(none) initializes((0, 4)) [[ARG:%.*]]) #[[ATTR15:[0-9]+]] {
; FNATTRS-NEXT: store i32 0, ptr [[ARG]], align 4
-; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR19]]
+; FNATTRS-NEXT: call void @fn_inaccessiblememonly() #[[ATTR20]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: nosync memory(argmem: readwrite, inaccessiblemem: readwrite)
@@ -518,9 +518,9 @@ entry:
; FIXME: This could be `memory(argmem: read)`.
define i64 @select_different_obj(i1 %c, ptr %p, ptr %p2) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i64 @select_different_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR3]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR19:[0-9]+]] {
; FNATTRS-NEXT: entry:
; FNATTRS-NEXT: [[P3:%.*]] = select i1 [[C]], ptr [[P]], ptr [[P2]]
; FNATTRS-NEXT: [[R:%.*]] = load i64, ptr [[P3]], align 4
@@ -580,9 +580,9 @@ join:
; FIXME: This could be `memory(argmem: read)`.
define i64 @phi_different_obj(i1 %c, ptr %p, ptr %p2) {
-; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none)
+; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(read, inaccessiblemem: none, errnomem: none)
; FNATTRS-LABEL: define i64 @phi_different_obj
-; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR3]] {
+; FNATTRS-SAME: (i1 [[C:%.*]], ptr readonly captures(none) [[P:%.*]], ptr readonly captures(none) [[P2:%.*]]) #[[ATTR19]] {
; FNATTRS-NEXT: entry:
; FNATTRS-NEXT: br i1 [[C]], label [[IF:%.*]], label [[JOIN:%.*]]
; FNATTRS: if:
diff --git a/llvm/test/Transforms/FunctionAttrs/errnomemnone.ll b/llvm/test/Transforms/FunctionAttrs/errnomemnone.ll
new file mode 100644
index 0000000000000..be965e5f55340
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/errnomemnone.ll
@@ -0,0 +1,30 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=FNATTRS %s
+
+; Stack-allocated memory cannot alias errno.
+define float @cannot_alias_errno(float %f) {
+; FNATTRS: Function Attrs: memory(readwrite, errnomem: none)
+; FNATTRS-LABEL: define float @cannot_alias_errno
+; FNATTRS-SAME: (float [[F:%.*]]) #[[ATTR0:[0-9]+]] {
+ %p = alloca float
+ call void @escape(ptr %p)
+ store float 0.0, ptr %p
+ call float @sinf(float %f)
+ %v = load float, ptr %p
+ ret float %v
+}
+
+; Accesses to memory larger than integer-size cannot alias errno.
+define double @cannot_alias_errno_2(ptr %p, float %f) {
+; FNATTRS: Function Attrs: memory(readwrite, errnomem: none)
+; FNATTRS-LABEL: define double @cannot_alias_errno_2
+; FNATTRS-SAME: (ptr [[P:%.*]], float [[F:%.*]]) #[[ATTR0]] {
+ call void @escape(ptr %p)
+ store double 0.0, ptr %p
+ call float @sinf(float %f)
+ %v = load double, ptr %p
+ ret double %v
+}
+
+declare void @escape(ptr %p)
+declare float @sinf(float) memory(errnomem: write)
diff --git a/llvm/test/Transforms/FunctionAttrs/initializes.ll b/llvm/test/Transforms/FunctionAttrs/initializes.ll
index 861c61d683ae0..b3362589d3f8b 100644
--- a/llvm/test/Transforms/FunctionAttrs/initializes.ll
+++ b/llvm/test/Transforms/FunctionAttrs/initializes.ll
@@ -91,8 +91,9 @@ define void @partial_load_before_store(ptr %p) {
declare void @use(ptr)
define void @call_clobber(ptr %p) {
+; CHECK: Function Attrs: memory(readwrite, errnomem: none)
; CHECK-LABEL: define void @call_clobber(
-; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: call void @use(ptr [[P]])
; CHECK-NEXT: store i64 123, ptr [[P]], align 4
; CHECK-NEXT: ret void
@@ -130,7 +131,7 @@ define void @store_offset(ptr %p) {
define void @store_volatile(ptr %p) {
; CHECK: Function Attrs: nofree norecurse nounwind memory(argmem: readwrite, inaccessiblemem: readwrite)
; CHECK-LABEL: define void @store_volatile(
-; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR3:[0-9]+]] {
; CHECK-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P]], i64 8
; CHECK-NEXT: store volatile i32 123, ptr [[G]], align 4
; CHECK-NEXT: ret void
@@ -241,8 +242,9 @@ end:
}
define void @call_clobber_on_one_branch(ptr %p, i1 %i) {
+; CHECK: Function Attrs: memory(readwrite, errnomem: none)
; CHECK-LABEL: define void @call_clobber_on_one_branch(
-; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) {
+; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
@@ -306,8 +308,9 @@ define void @non_const_gep(ptr %p, i64 %i) {
}
define void @call_clobber_in_entry_block(ptr %p, i1 %i) {
+; CHECK: Function Attrs: memory(readwrite, errnomem: none)
; CHECK-LABEL: define void @call_clobber_in_entry_block(
-; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) {
+; CHECK-SAME: ptr [[P:%.*]], i1 [[I:%.*]]) #[[ATTR2]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: call void @use(ptr [[P]])
; CHECK-NEXT: br i1 [[I]], label [[BB1:%.*]], label [[BB2:%.*]]
@@ -384,7 +387,7 @@ define void @call_initializes_escape_bundle(ptr %p) {
define void @access_bundle() {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define void @access_bundle(
-; CHECK-SAME: ) #[[ATTR3:[0-9]+]] {
+; CHECK-SAME: ) #[[ATTR4:[0-9]+]] {
; CHECK-NEXT: [[SINK:%.*]] = alloca i64, align 8
; CHECK-NEXT: store i64 123, ptr [[SINK]], align 4
; CHECK-NEXT: ret void
@@ -397,7 +400,7 @@ define void @access_bundle() {
define void @call_operand_bundle(ptr %p) {
; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn
; CHECK-LABEL: define void @call_operand_bundle(
-; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR5:[0-9]+]] {
; CHECK-NEXT: call void @access_bundle() [ "unknown"(ptr [[P]]) ]
; CHECK-NEXT: ret void
;
@@ -445,7 +448,7 @@ define void @memset_neg(ptr %p) {
define void @memset_volatile(ptr %p) {
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define void @memset_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]]) #[[ATTR6:[0-9]+]] {
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P]], i8 2, i64 9, i1 true)
; CHECK-NEXT: ret void
;
@@ -480,7 +483,7 @@ define void @memcpy(ptr %p, ptr %p2) {
define void @memcpy_volatile(ptr %p, ptr %p2) {
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
; CHECK-LABEL: define void @memcpy_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR7:[0-9]+]] {
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
; CHECK-NEXT: ret void
;
@@ -543,7 +546,7 @@ define void @memmove(ptr %p, ptr %p2) {
define void @memmove_volatile(ptr %p, ptr %p2) {
; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite)
; CHECK-LABEL: define void @memmove_volatile(
-; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR6]] {
+; CHECK-SAME: ptr writeonly [[P:%.*]], ptr readonly [[P2:%.*]]) #[[ATTR7]] {
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr [[P]], ptr [[P2]], i64 9, i1 true)
; CHECK-NEXT: ret void
;
diff --git a/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll b/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll
new file mode 100644
index 0000000000000..db8effc45442f
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/read-write-errnomem.ll
@@ -0,0 +1,71 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2
+; RUN: opt -passes=function-attrs -S < %s | FileCheck --check-prefixes=FNATTRS %s
+; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=ATTRIBUTOR %s
+
+define i32 @test_read_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: read)
+; FNATTRS-LABEL: define i32 @test_read_errno
+; FNATTRS-SAME: () #[[ATTR0:[0-9]+]] {
+; FNATTRS-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4:[0-9]+]]
+; FNATTRS-NEXT: [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; FNATTRS-NEXT: ret i32 [[ERRNO]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(read)
+; ATTRIBUTOR-LABEL: define i32 @test_read_errno
+; ATTRIBUTOR-SAME: () #[[ATTR0:[0-9]+]] {
+; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4:[0-9]+]]
+; ATTRIBUTOR-NEXT: [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT: ret i32 [[ERRNO]]
+;
+ %call = call ptr @__errno_location() #2
+ %errno = load i32, ptr %call
+ ret i32 %errno
+}
+
+define ptr @test_write_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: write)
+; FNATTRS-LABEL: define noundef ptr @test_write_errno
+; FNATTRS-SAME: () #[[ATTR1:[0-9]+]] {
+; FNATTRS-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4]]
+; FNATTRS-NEXT: store i32 0, ptr [[CALL]], align 4
+; FNATTRS-NEXT: ret ptr [[CALL]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)
+; ATTRIBUTOR-LABEL: define ptr @test_write_errno
+; ATTRIBUTOR-SAME: () #[[ATTR1:[0-9]+]] {
+; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4]]
+; ATTRIBUTOR-NEXT: store i32 0, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT: ret ptr [[CALL]]
+;
+ %call = call ptr @__errno_location() #2
+ store i32 0, ptr %call
+ ret ptr %call
+}
+
+define i32 @test_readwrite_errno() {
+; FNATTRS: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(errnomem: readwrite)
+; FNATTRS-LABEL: define i32 @test_readwrite_errno
+; FNATTRS-SAME: () #[[ATTR2:[0-9]+]] {
+; FNATTRS-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4]]
+; FNATTRS-NEXT: store i32 0, ptr [[CALL]], align 4
+; FNATTRS-NEXT: [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; FNATTRS-NEXT: ret i32 [[ERRNO]]
+;
+; ATTRIBUTOR: Function Attrs: mustprogress nofree nosync nounwind willreturn
+; ATTRIBUTOR-LABEL: define i32 @test_readwrite_errno
+; ATTRIBUTOR-SAME: () #[[ATTR2:[0-9]+]] {
+; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call ptr @__errno_location() #[[ATTR4]]
+; ATTRIBUTOR-NEXT: store i32 0, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT: [[ERRNO:%.*]] = load i32, ptr [[CALL]], align 4
+; ATTRIBUTOR-NEXT: ret i32 [[ERRNO]]
+;
+ %call = call ptr @__errno_location() #2
+ store i32 0, ptr %call
+ %errno = load i32, ptr %call
+ ret i32 %errno
+}
+
+declare ptr @__errno_location() #1
+
+attributes #1 = { mustprogress nofree nosync nounwind willreturn memory(none) }
+attributes #2 = { nounwind willreturn memory(none) }
diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
index b24c097ad54d0..ec6e116837478 100644
--- a/llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -376,8 +376,9 @@ declare void @escape_readonly_ptr(ptr %addr, ptr readonly %ptr)
; is marked as readnone/only. However, the functions can write the pointer into
; %addr, causing the store to write to %escaped_then_written.
define void @unsound_readnone(ptr %ignored, ptr %escaped_then_written) {
+; FNATTRS: Function Attrs: memory(readwrite, errnomem: write)
; FNATTRS-LABEL: define {{[^@]+}}@unsound_readnone
-; FNATTRS-SAME: (ptr readnone captures(none) [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; FNATTRS-SAME: (ptr readnone captures(none) [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) #[[ATTR14:[0-9]+]] {
; FNATTRS-NEXT: [[ADDR:%.*]] = alloca ptr, align 8
; FNATTRS-NEXT: call void @escape_readnone_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
; FNATTRS-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
@@ -408,8 +409,9 @@ define void @unsound_readnone(ptr %ignored, ptr %escaped_then_written) {
}
define void @unsound_readonly(ptr %ignored, ptr %escaped_then_written) {
+; FNATTRS: Function Attrs: memory(readwrite, errnomem: write)
; FNATTRS-LABEL: define {{[^@]+}}@unsound_readonly
-; FNATTRS-SAME: (ptr readnone captures(none) [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) {
+; FNATTRS-SAME: (ptr readnone captures(none) [[IGNORED:%.*]], ptr [[ESCAPED_THEN_WRITTEN:%.*]]) #[[ATTR14]] {
; FNATTRS-NEXT: [[ADDR:%.*]] = alloca ptr, align 8
; FNATTRS-NEXT: call void @escape_readonly_ptr(ptr [[ADDR]], ptr [[ESCAPED_THEN_WRITTEN]])
; FNATTRS-NEXT: [[ADDR_LD:%.*]] = load ptr, ptr [[ADDR]], align 8
@@ -570,7 +572,7 @@ define void @fptr_test2c(ptr %p, ptr %f) {
define void @alloca_recphi() {
; FNATTRS: Function Attrs: nofree norecurse nosync nounwind memory(none)
; FNATTRS-LABEL: define {{[^@]+}}@alloca_recphi
-; FNATTRS-SAME: () #[[ATTR14:[0-9]+]] {
+; FNATTRS-SAME: () #[[ATTR15:[0-9]+]] {
; FNATTRS-NEXT: entry:
; FNATTRS-NEXT: [[A:%.*]] = alloca [8 x i32], align 4
; FNATTRS-NEXT: [[A_END:%.*]] = getelementptr i32, ptr [[A]], i64 8
@@ -723,7 +725,7 @@ define void @op_bundle_readonly_unknown(ptr %p) {
define i32 @writable_readonly(ptr writable dereferenceable(4) %p) {
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read)
; FNATTRS-LABEL: define {{[^@]+}}@writable_readonly
-; FNATTRS-SAME: (ptr readonly captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR15:[0-9]+]] {
+; FNATTRS-SAME: (ptr readonly captures(none) dereferenceable(4) [[P:%.*]]) #[[ATTR16:[0-9]+]] {
; FNATTRS-NEXT: [[V:%.*]] = load i32, ptr [[P]], align 4
; FNATTRS-NEXT: ret i32 [[V]]
;
diff --git a/llvm/test/Transforms/PhaseOrdering/pr95152.ll b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
index 1e28856f32bd4..9c9ebd2dff3f5 100644
--- a/llvm/test/Transforms/PhaseOrdering/pr95152.ll
+++ b/llvm/test/Transforms/PhaseOrdering/pr95152.ll
@@ -45,7 +45,7 @@ define void @g(ptr dead_on_unwind noalias writable dereferenceable(8) align 8 %p
define void @f(ptr dead_on_unwind noalias %p) {
; CHECK-LABEL: define void @f(
-; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr {
+; CHECK-SAME: ptr dead_on_unwind noalias initializes((0, 8)) [[P:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
; CHECK-NEXT: store i64 3, ptr [[P]], align 4
; CHECK-NEXT: tail call void @j(ptr nonnull align 8 dereferenceable(8) [[P]])
; CHECK-NEXT: store i64 43, ptr [[P]], align 4
|
| const Value *UO = getUnderlyingObjectAggressive(Loc.Ptr); | ||
| if (isa<AllocaInst>(UO)) | ||
| if (isa<AllocaInst>(UO)) { | ||
| ME -= ME.errnoMemOnly(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what is going on here. You're saying that any function that accesses an alloca doesn't accesses errno?
Generally, it doesn't make sense to mask out something from ME after the fact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the original intent was to try to remove errno, if, say, an argument ptr is an alloca for that memory location; which is however not what is happening here. Looking at this better again, I think there's not much we can reliably infer here, and, once again, it should be up to getModRefInfo to determine whether the underlying ptr is alloca memory? Updated this, meanwhile.
872a7e9 to
228a06c
Compare
errno inference logicerrnomem location when accessing errno
|
Kind ping. |
nikic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure whether this is really useful for anything...
| if (auto *CI = dyn_cast<CallInst>(UO)) { | ||
| if (auto *Callee = CI->getCalledFunction(); Callee && Callee->hasName()) { | ||
| static constexpr auto ErrnoFnNames = {"__errno_location", "_errno", | ||
| "__errno", "___errno"}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to go through TLI.
| ; RUN: opt -passes=attributor-light -S < %s | FileCheck --check-prefixes=ATTRIBUTOR %s | ||
|
|
||
| define i32 @test_read_errno() { | ||
| ; FNATTRS-LABEL: define i32 @test_read_errno() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this isn't actually testing the inferred attributes. You need to pass --function-attrs or something like that to UTC.
Deduce `errnomem` for a given memory location: accesses to `__errno_location` et alia do access `errnomem` memory.
228a06c to
2c0fd7a
Compare
|
Updated, thanks.
Admittedly I felt like a bit the same while updating this the last time, perhaps user-defined wrapper functions around errno may benefit from this? I'll let you establish whether this may be worth having or not. |
I'm leaning towards "no", at least until we have some use-case where this would help optimization. |
Deduce
errnomemfor a given memory location: accesses to__errno_locationet alia do accesserrnomemmemory.